//**************************************************
// Description: generate IP frame
//**************************************************

//**************************************************
// include files
//**************************************************
`include "protocol_define.v"

module tx_IP_generator #(
    parameter IN_DATA_WIDTH         = 128       ,
    parameter IN_MOD_WIDTH          = 4         ,
    parameter DATALINK_IPV4         = 16'h0800  ,
    parameter IP_VERSION            = 4'h4      ,
    parameter IP_HEADER_LENGTH      = 4'h5      ,
    parameter IP_TOS                = 8'h00     ,
    // IP_TOTAL_LENGTH(16)
    parameter IP_IDENTIFICATION     = 16'h444c  ,
    parameter IP_FLAG               = 3'b000    ,
    parameter IP_OFFSET             = 13'h0000  ,
    parameter IP_TTL                = 8'hff     ,
    parameter IP_PROTOCOL           = 8'h11     
    // IP_HEADER_CHECKSUM           = 
    ) ( 
    input                                       clk ,
    input                                       rst_n ,
    // pre-module(UDP gen module) interface
    output                                      IP_gen_rdy ,
    input       [IN_DATA_WIDTH-1:0]             IP_generator_data ,
    input       [IN_MOD_WIDTH-1:0]              IP_generator_mod ,
    input                                       IP_generator_sav ,
    input                                       IP_generator_val ,
    input                                       IP_generator_sop ,
    input                                       IP_generator_eop ,
    input       [10:0]                          IP_generator_length_in ,
    input       [15:0]                          IP_generator_checksum_in , // no use
    input       [47:0]                          IP_generator_dst_mac ,
    input       [47:0]                          IP_generator_src_mac ,
    input       [31:0]                          IP_generator_src_IP ,
    input       [31:0]                          IP_generator_dst_IP ,
    // post-module(tx arbiter) interface
    input                                       UDP_frame_rdy , 
    output      [IN_DATA_WIDTH-1:0]             UDP_frame_data ,
    output      [IN_MOD_WIDTH-1:0]              UDP_frame_mod , 
    output                                      UDP_frame_sav , 
    output                                      UDP_frame_val , 
    output                                      UDP_frame_sop , 
    output                                      UDP_frame_eop
) ;
    //----------------------------------------------
    // input signal declare
    //----------------------------------------------
    // frame info
    reg     [10:0]                  IP_generator_length_in_ff   ;
    reg     [47:0]                  IP_generator_dst_mac_ff     ;
    reg     [47:0]                  IP_generator_src_mac_ff     ;
    reg     [31:0]                  IP_generator_src_IP_ff      ; 
    reg     [31:0]                  IP_generator_dst_IP_ff      ; 
    // input data 
    reg     [IN_DATA_WIDTH-1:0]     IP_generator_data_ff1 ;
    reg     [IN_DATA_WIDTH-1:0]     IP_generator_data_ff2 ;
    reg     [IN_DATA_WIDTH-1:0]     IP_generator_data_ff3 ;
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n) begin
            IP_generator_data_ff1 <= {(IN_DATA_WIDTH){1'b0}} ;
            IP_generator_data_ff2 <= {(IN_DATA_WIDTH){1'b0}} ;
            IP_generator_data_ff3 <= {(IN_DATA_WIDTH){1'b0}} ;
        end
        else begin
            IP_generator_data_ff1 <= IP_generator_data      ;
            IP_generator_data_ff2 <= IP_generator_data_ff1  ;
            IP_generator_data_ff3 <= IP_generator_data_ff2  ;
        end
    end

    //----------------------------------------------
    // output signal declare
    //----------------------------------------------
    reg     [IN_DATA_WIDTH-1:0]     UDP_frame_data_r    ;
    reg     [IN_MOD_WIDTH-1:0]      UDP_frame_mod_r     ;
    reg                             UDP_frame_sav_r     ;
    reg                             UDP_frame_val_r     ;
    reg                             UDP_frame_sop_r     ;
    reg                             UDP_frame_eop_r     ;

    //----------------------------------------------
    // internal signal
    //----------------------------------------------
    // checksum
    reg     [31:0]                  IP_header_chechsum ;
    wire    IP_checksum_all_zero ;
    assign  IP_checksum_all_zero = (IP_header_chechsum[31:16]==16'b0) ;
    // first and second transfer
    reg                             first_transfer ;
    reg                             second_transfer ;

//**************************************************
// FSM 
//**************************************************
    localparam IP_GEN_IDLE          = 3'd0 ;
    localparam IP_GEN_HEADER        = 3'd1 ;
    localparam IP_GEN_GET_FRAME     = 3'd2 ;
    localparam IP_GEN_SEND_FRAME    = 3'd3 ;
    localparam IP_GEN_SEND_LAST1    = 3'd4 ;
    localparam IP_GEN_SEND_LAST2    = 3'd5 ;
    localparam IP_GEN_END           = 3'd6 ;

    reg     [2:0]                   ip_gen_state_c ;
    reg     [2:0]                   ip_gen_state_n ;

    // FSM(1)
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)  
            ip_gen_state_c <= IP_GEN_IDLE ;
        else
            ip_gen_state_c <= ip_gen_state_n ;
    end
    // FSM(2)
    always @(*) begin
        case (ip_gen_state_c)
            IP_GEN_IDLE: begin
                if(IP_generator_sav && UDP_frame_rdy)
                    ip_gen_state_n = IP_GEN_HEADER ;
                else
                    ip_gen_state_n = IP_GEN_IDLE ;
            end

            IP_GEN_HEADER: begin
                if(IP_checksum_all_zero)
                    ip_gen_state_n = IP_GEN_GET_FRAME ;
                else
                    ip_gen_state_n = IP_GEN_HEADER ;
            end

            IP_GEN_GET_FRAME: begin
                if(IP_generator_sop)
                    ip_gen_state_n = IP_GEN_SEND_FRAME ;
                else
                    ip_gen_state_n = IP_GEN_GET_FRAME ;
            end

            IP_GEN_SEND_FRAME: begin
                if(IP_generator_eop) 
                    ip_gen_state_n = IP_GEN_SEND_LAST1 ;
                else
                    ip_gen_state_n = IP_GEN_SEND_FRAME ;
            end

            IP_GEN_SEND_LAST1: begin
                ip_gen_state_n = IP_GEN_SEND_LAST2 ;
            end

            IP_GEN_SEND_LAST2: begin
                ip_gen_state_n = IP_GEN_END ;
            end

            IP_GEN_END: begin
                ip_gen_state_n = IP_GEN_IDLE ;
            end 

            default: 
                ip_gen_state_n = IP_GEN_IDLE ;
        endcase
    end
    // FSM(3)
    // first transfer
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)
            first_transfer <= 1'b1 ;
        else if( ip_gen_state_c==IP_GEN_IDLE )
            first_transfer <= 1'b1 ;
        else if( ip_gen_state_c==IP_GEN_SEND_FRAME )
            first_transfer <= 1'b0 ;
        else
            first_transfer <= first_transfer ;
    end
    // second transfer
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)
            second_transfer <= 1'b1 ;
        else if( ip_gen_state_c==IP_GEN_IDLE )
            second_transfer <= 1'b1 ;
        else if( (ip_gen_state_c==IP_GEN_SEND_FRAME) && (!first_transfer) )
            second_transfer <= 1'b0 ;
        else
            second_transfer <= second_transfer ;
    end
    // checksum
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n) 
            IP_header_chechsum <= 32'b0 ;
        else if( (ip_gen_state_c==IP_GEN_IDLE) && (IP_generator_sav && UDP_frame_rdy) )
            IP_header_chechsum <= ( {IP_VERSION, IP_HEADER_LENGTH, IP_TOS}  + 
                                    {5'b0, (IP_generator_length_in+11'd20)} + 
                                    {IP_IDENTIFICATION}                     + 
                                    {IP_FLAG, IP_OFFSET}                    +
                                    {IP_TTL, IP_PROTOCOL}                   +
                                    {IP_generator_src_IP[31:16]}            +
                                    {IP_generator_src_IP[15:0]}             +
                                    {IP_generator_dst_IP[31:16]}            +
                                    {IP_generator_dst_IP[15:0]} )           ;
        else if( ip_gen_state_c==IP_GEN_HEADER )
            IP_header_chechsum <= IP_header_chechsum[31:16] + IP_header_chechsum[15:0] ;
        else
            IP_header_chechsum <= IP_header_chechsum ;
    end
    // "data"
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)
            UDP_frame_data_r <= {(IN_DATA_WIDTH){1'b0}} ;
        else if( (ip_gen_state_c==IP_GEN_GET_FRAME) && IP_generator_sop )
                // the first transfer must be: DSM_MAC+SRC_MAC+ETH_TYPE+16-bitIP
            UDP_frame_data_r <= { IP_generator_dst_mac_ff, IP_generator_src_mac_ff, 
                                DATALINK_IPV4, {IP_VERSION, IP_HEADER_LENGTH, IP_TOS} } ;
        else if( (ip_gen_state_c==IP_GEN_SEND_FRAME) && first_transfer )
                // the second transfer must be: 16-byte IP
            UDP_frame_data_r <= { {5'b0, IP_generator_length_in_ff, IP_IDENTIFICATION} ,
                                {IP_FLAG, IP_OFFSET, IP_TTL, IP_PROTOCOL} ,
                                {IP_header_chechsum[15:0], IP_generator_src_IP_ff[31:16]} ,
                                {IP_generator_src_IP_ff[15:0], IP_generator_dst_IP_ff[31:16]} } ;
        else if( (ip_gen_state_c==IP_GEN_SEND_FRAME) && second_transfer )
                // the second transfer must be: 2-byte IP + 14-byte ff2 valid data(first data)
            UDP_frame_data_r <= { {IP_generator_dst_IP_ff[15:0], IP_generator_data_ff2[127:16]} } ;
        else if( (ip_gen_state_c==IP_GEN_SEND_FRAME) )
                // the forth transfer1(no eop): 2-byte ff3 valid data + 14-byte ff2 valid data(second data)
            UDP_frame_data_r <= { {IP_generator_data_ff3[15:0], IP_generator_data_ff2[127:16]} } ;
        else if( (ip_gen_state_c==IP_GEN_SEND_LAST1) )
                // the forth transfer2(eop): 2-byte ff3 valid data + 14-byte ff2 valid data(second data)
            UDP_frame_data_r <= { {IP_generator_data_ff3[15:0], IP_generator_data_ff2[127:16]} } ;
        else if( (ip_gen_state_c==IP_GEN_SEND_LAST2) )
                // 2-byte ff3 valid data + 14-byte ff2 valid data(third data)
            UDP_frame_data_r <= { {IP_generator_data_ff3[15:0], IP_generator_data_ff2[127:16]} } ;
        else
            UDP_frame_data_r <= {(IN_DATA_WIDTH){1'b0}} ;
    end
    // "mod"
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)
            UDP_frame_mod_r <= {(IN_MOD_WIDTH){1'b0}} ;
        else if( (ip_gen_state_c==IP_GEN_SEND_LAST2) )
            UDP_frame_mod_r <= 4'd10 ;
        else
            UDP_frame_mod_r <= {(IN_MOD_WIDTH){1'b0}} ;
    end
    // "sav"
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)
            UDP_frame_sav_r <= 1'b0 ;
        else if( (ip_gen_state_c==IP_GEN_IDLE) && (IP_generator_sav && UDP_frame_rdy) )
            UDP_frame_sav_r <= 1'b1 ;
        else if( (ip_gen_state_c==IP_GEN_END) )
            UDP_frame_sav_r <= 1'b0 ;
        else 
            UDP_frame_sav_r <= UDP_frame_sav_r ;
    end
    // "val"
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)
            UDP_frame_val_r <= 1'b0 ;
        else if( (ip_gen_state_c==IP_GEN_GET_FRAME) && IP_generator_sop )
            UDP_frame_val_r <= 1'b1 ;
        else if( (ip_gen_state_c==IP_GEN_END) )
            UDP_frame_val_r <= 1'b0 ;
        else 
            UDP_frame_val_r <= UDP_frame_val_r ;
    end
    // "sop"
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)
            UDP_frame_sop_r <= 1'b0 ;
        else if( (ip_gen_state_c==IP_GEN_GET_FRAME) && IP_generator_sop )
            UDP_frame_sop_r <= 1'b1 ;
        else
            UDP_frame_sop_r <= 1'b0 ;
    end
    // "eop"
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)
            UDP_frame_eop_r <= 1'b0 ;
        else if( (ip_gen_state_c==IP_GEN_SEND_LAST2) )
            UDP_frame_eop_r <= 1'b1 ;
        else
            UDP_frame_eop_r <= 1'b0 ;
    end


    // input delay
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n) begin
            IP_generator_length_in_ff <= 11'b0 ;
            IP_generator_dst_mac_ff <= 48'b0 ;
            IP_generator_src_mac_ff <= 48'b0 ;
            IP_generator_src_IP_ff <= 32'b0 ;
            IP_generator_dst_IP_ff <= 32'b0 ;
        end
        else if( (ip_gen_state_c==IP_GEN_IDLE) && (IP_generator_sav && UDP_frame_rdy) ) begin
            IP_generator_length_in_ff <= IP_generator_length_in + 11'd20 ;
            IP_generator_dst_mac_ff <= IP_generator_dst_mac ;
            IP_generator_src_mac_ff <= IP_generator_src_mac ;
            IP_generator_src_IP_ff <= IP_generator_src_IP ;
            IP_generator_dst_IP_ff <= IP_generator_dst_IP ;
        end
        else begin
            IP_generator_length_in_ff <= IP_generator_length_in_ff ;
            IP_generator_dst_mac_ff <= IP_generator_dst_mac_ff ;
            IP_generator_src_mac_ff <= IP_generator_src_mac_ff ;
            IP_generator_src_IP_ff <= IP_generator_src_IP_ff ;
            IP_generator_dst_IP_ff <= IP_generator_dst_IP_ff ;
        end
    end

//**************************************************
// output assign 
//**************************************************
    assign IP_gen_rdy       = (ip_gen_state_c==IP_GEN_IDLE)?1'b1:1'b0 ; 
    assign UDP_frame_data   = UDP_frame_data_r ;
    assign UDP_frame_mod    = UDP_frame_mod_r ;
    assign UDP_frame_sav    = UDP_frame_sav_r ;
    assign UDP_frame_val    = UDP_frame_val_r ;
    assign UDP_frame_sop    = UDP_frame_sop_r ;
    assign UDP_frame_eop    = UDP_frame_eop_r ;

endmodule

